/******************************************************************************
Copyright:
2009 Jens Collin (jens.collin@dinomin.com)

This file is part of Envox Decompiler.

Envox Decompiler is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Envox Decompiler is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with KitchenOS.  If not, see <http://www.gnu.org/licenses/>.

******************************************************************************/
using System;
using System.Collections;

namespace EnvoxDecompiler
{
	/// <summary>
	/// Summary description for BlockFactory.
	/// </summary>
	public sealed class BlockFactory
	{
		private static BlockFactory instance = new BlockFactory();

		private BlockFactory()
		{
		}

		public static BlockFactory Instance 
		{
			get 
			{
				return instance;
			}
		}


		// Walk backwards and find the last added block of the passed type.
		private Block GetLastBlock(Block.TYPE blockType, ArrayList blocks)
		{
			for (int i= blocks.Count-1; i > 0; i--)
			{
				Block tb = (Block)blocks[i];
				if (tb.GetBlockType() == blockType)
				{
					Trace.Debug("GetLastBlock: Found block of type " + blockType + " at position " + i + " in blocks.");
					return tb;
				}
			}

			Trace.Debug("GetLastBlock: Can't find any block of type " + blockType);
			return null;
		}


		// Find last added FindFilesFirst block and add this data to that block
		private Block AppendToFirst(Block.TYPE blockType, byte[] rawData, ArrayList blocks)
		{
			Block lastBlock = GetLastBlock(blockType, blocks);
			if (lastBlock == null)
			{
				Trace.Debug("AppendToFirst: Couldn't get last block, this will not work!");
				Trace.Info("Invalid seqences for paired blocks, expecting init-block to exist.");
				Settings.Instance.AddProblemToInfoNote("Manually check all blocks of type: "
					+ blockType + " in resulting .ens file, they will probably be wrong.");
				return null;
			}
			
			lastBlock.AppendNextData(rawData);
			return lastBlock;
		}



		public Block CreateBlock(byte[] rawData, ArrayList blocks)
		{
			Trace.Debug("CreateBlock started");

			Block.TYPE type = (Block.TYPE)rawData[30];
			Block returnBlock = null;
			switch (type)
			{
				case Block.TYPE.Start:
					returnBlock = new Block_Start(rawData);
					break;

				case Block.TYPE.Stop:
					returnBlock = new Block_Stop(rawData);
					break;

				case Block.TYPE.EventCheck:
					returnBlock = new Block_EventCheck(rawData);
					break;

				case Block.TYPE.EventProcess:
					returnBlock = new Block_EventProcess(rawData);
					break;

				case Block.TYPE.Delay:
					returnBlock = new Block_Delay(rawData);
					break;

				case Block.TYPE.SetVariable:
					returnBlock = new Block_SetVariable(rawData);
					break;

				case Block.TYPE.TimeSwitch:
					returnBlock = new Block_TimeSwitch(rawData);
					break;

				case Block.TYPE.DBOpen:
					returnBlock = new Block_DBOpen(rawData);
					break;

				case Block.TYPE.DBSelect:
					returnBlock = new Block_DBSelect(rawData);
					break;

				case Block.TYPE.DBBrowse:
					returnBlock = new Block_DBBrowse(rawData);
					break;

				case Block.TYPE.DBClose:
					returnBlock = new Block_DBClose(rawData);
					break;

				case Block.TYPE.DBSQL:
					returnBlock = new Block_DBSQL(rawData);
					break;

				case Block.TYPE.DBTransBegin:
					returnBlock = new Block_DBTransBegin(rawData);
					break;
					
				case Block.TYPE.DBTransEnd:
					returnBlock = new Block_DBTransEnd(rawData);
					break;
					
				case Block.TYPE.FindFiles:
					returnBlock = new Block_FindFiles(rawData);
					break;

				// Special handling of findNext since it is a part of FindFilesFirst in studio.
				case Block.TYPE.FindFilesNext:
					returnBlock = AppendToFirst(Block.TYPE.FindFiles, rawData, blocks);
					break;

				case Block.TYPE.FileRead:
					returnBlock = new Block_FileRead(rawData);
					break;

				case Block.TYPE.FileProperties:
					returnBlock = new Block_FileProperties(rawData);
					break;

				// Special handling of FileReadNext since it is a part of FileRead in studio.
				case Block.TYPE.FileReadNext:
					returnBlock = AppendToFirst(Block.TYPE.FileRead, rawData, blocks);
					break;
				
				case Block.TYPE.ChangeScript:
					returnBlock = new Block_ChangeScript(rawData);
					break;

				case Block.TYPE.Counter:
					returnBlock = new Block_Counter(rawData);
					break;

				case Block.TYPE.WaitCall:
					returnBlock = new Block_WaitCall(rawData);
					break;

				case Block.TYPE.XMLCreateDocument:
					returnBlock = new Block_XMLCreateDocument(rawData);
					break;

				case Block.TYPE.XMLCreateNode:
					returnBlock = new Block_XMLCreateNode(rawData);
					break;

				case Block.TYPE.XMLModifyNode:
					returnBlock = new Block_XMLModifyNode(rawData);
					break;
					
				case Block.TYPE.XMLCloseHandle:
					returnBlock = new Block_XMLCloseHandle(rawData);
					break;
					
				case Block.TYPE.XMLSaveDocument:
					returnBlock = new Block_XMLSaveDocument(rawData);
					break;
					
				case Block.TYPE.XMLSetAttributes:
					returnBlock = new Block_XMLSetAttributes(rawData);
					break;
					
				case Block.TYPE.TcpIpListen:
					returnBlock = new Block_TcpIpListen(rawData);
					break;
					
				case Block.TYPE.TcpIpConnect:
					returnBlock = new Block_TcpIpConnect(rawData);
					break;

				case Block.TYPE.TcpIpClose:
					returnBlock = new Block_TcpIpClose(rawData);
					break;
					
				case Block.TYPE.TcpIpSend:
					returnBlock = new Block_TcpIpSend(rawData);
					break;
					
				case Block.TYPE.TcpIpReceive:
					returnBlock = new Block_TcpIpReceive(rawData);
					break;
					
				case Block.TYPE.Registry:
					returnBlock = new Block_Registry(rawData);
					break;

				case Block.TYPE.Shutdown:
					returnBlock = new Block_Shutdown(rawData);
					break;
					
				case Block.TYPE.RunHostedScript:
					returnBlock = new Block_RunHostedScript(rawData);
					break;

                case Block.TYPE.SplitVariable:
                    returnBlock = new Block_SplitVariable(rawData);
					break;

				default:
					Trace.Error("Unsupported block of type " + type + " found!");
					Trace.Error("This will make the output script uncompilable!");
					Settings.Instance.Unimplemented("Unsupported block of type " + type + " found!");
					break;
			}

			if (returnBlock != null)
			{
				blocks.Add(returnBlock);
			}
			return returnBlock;
		}
	}
}
